#define _WIN32_WINNT 0x0501


#define _socketio_closerr() PyErr_Format(PyExc_ValueError, "I/O operation on closed socket")
#define _socketio_moderr(action) PyErr_Format(PyExc_ValueError, "Socket not open for %s", action)
#if defined(PLAT_WINNT)
#define _socketio_ioerr() PyErr_SetExcFromWindowsErr(PyExc_IOError, 0)
#elif defined(PLAT_POSIX)
#define _socketio_ioerr() PyErr_SetFromErrno(PyExc_IOError)
#endif

/*
static PyObject *
_socketio_closerror()
{
    return PyErr_Format(PyExc_ValueError, "I/O operation on closed socket");
}

static PyObject *
_socketio_moderror(char *action)
{
    return PyErr_Format(PyExc_ValueError, "Socket not open for %s", action);
}

static PyObject *
_socketio_ioerror()
{
#ifdef MS_WINDOWS
	return PyErr_SetExcFromWindowsErr(PyExc_IOError, 0);
#else
	return PyErr_SetFromErrno(PyExc_IOError);
#endif
}
*/
/*
typedef struct
{
	PyObject_HEAD
	union
	{
		struct sockaddr     sa;
		struct sockaddr_in  in;
#ifdef AF_INET6
		struct sockaddr_in6 in6;
#endif
#ifdef AF_UNIX
		struct sockaddr_un  un;
#endif
#ifdef AF_NETLINK
		struct sockaddr_nl  nl;
#endif
#ifdef AF_APPLETALK
		struct sockaddr_atalk at;
#endif
	} saddr;
} PySockAddrObject;
*/

/*
#define PySockAddr_sa(op) PySockAddr_addr(op, struct sockaddr)
#define PySockAddr_sin_new() PySockAddr_new(struct sockaddr_in)
#define PySockAddr_sin_addr(op) PySockAddr_addr(op, struct sockaddr_in)
#define PySockAddr_sin6(op) ((struct sockaddr_in6 *)((op)->ob_item))
#define PySockAddr_sun(op) ((struct sockaddr_un *)((op)->ob_item))

PyObject *
_sockaddr_create(const struct sockaddr *saddr, socklen_t addrlen)
{
	PySockAddrObject *self;
	switch (saddr->sa_family)
	{
	case AF_INET:
#ifdef AF_INET6
	case AF_INET6:
#endif
#ifdef AF_UNIX
	case AF_UNIX:
#endif
#ifdef AF_NETLINK
	case AF_NETLINK:
#endif
#ifdef AF_APPLETALK
	case AF_APPLETALK:
#endif
		if (addrlen <= sizeof(self->addr))
			break;
		return PyErr_Format(PyExc_ValueError, "address length");
	default:
		return PyErr_Format(PyExc_ValueError, "address family");
	}
	self = PyObject_New(PySockAddrObject, &PySockAddr_Type);
	if (self)
		memcpy(&self->addr, saddr, addrlen);
	return self;
}
*/

PyObject *
sockaddr_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
{
	PySockAddrObject *self;
//	Py_ssize_t idx, nitems;
//	static const char *kwlist[] = {"sequence", 0};

	if (!PyArg_ParseTupleAndKeywords(args, kwds, "|O:tuple", kwlist, &arg))
		return NULL;

	assert(PyType_IsSubtype(type, &PySockAddr_Type));
	self = (PySockAddrObject *) type->tp_alloc(type, 0);
	if (self == NULL)
		return NULL;

	self->addr.sa.sa_family = family;
	switch (family)
	{
	case AF_INET:
		if (arg2 == Py_None)
			self->addr.sin.sin_addr.s_addr = INADDR_ANY;
		else
		{
			uint32_t s_addr = PyInt_AsLong(arg2);
			if (s_addr == -1 && PyErr_Occurred())
				err;
			self->addr.sin.sin_addr.s_addr = s_addr;
		}
		self->addr.sin.sin_addr.s_addr = htonl(s_addr);
		if (arg3 == Py_None)
			port = 0;
		else
			port = PyInt_AsLong(arg3);
		self->addr.sin.sin_port = htons(port);			
		break;

	case AF_INET6:
		break;
	case AF_UNIX:
		break;
	case AF_APPLETALK:
		break;
	}
	self = type->tp_alloc(type, nitems);
	if (!self)
		return NULL;
	for (idx = 0; idx < nitems; idx++)
	{
		PyTuple_SET_ITEM(self, idx, item);
	}
	return self;
}

static int
sockaddr_init(PyObject *self, PyObject *args, PyObject *kwds)
{
	static char *kwlist[] = {"socket", "mode", "closefd", "timeout", 
							 "backlog", "openargs", NULL};

	assert(PySocketIO_Check(self));
	if (!_socketio_isclosed(self))
		pass;

	if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|si:socketio",
									 kwlist, &nameobj, &mode, &closefd))
		return -1;
}

typedef struct
{
    PyListObject list;
    int state;
} spamlistobject;


static PyTypeObject PySockAddr_Type =
{
    PyVarObject_HEAD_INIT(NULL, 0)
    "_ipcio.SocketAddr",
    0,
    0,
    0,                                          /* tp_dealloc */
    0,                                          /* tp_print */
    0,                                          /* tp_getattr */
    0,                                          /* tp_setattr */
    0,                                          /* tp_compare */
    0,                                          /* tp_repr */
    0,                                          /* tp_as_number */
    0,                                          /* tp_as_sequence */
    0,                                          /* tp_as_mapping */
    0,                                          /* tp_hash */
    0,                                          /* tp_call */
    0,                                          /* tp_str */
    0,                                          /* tp_getattro */
    0,                                          /* tp_setattro */
    0,                                          /* tp_as_buffer */
    Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
    0,                                          /* tp_doc */
    0,                                          /* tp_traverse */
    0,                                          /* tp_clear */
    0,                                          /* tp_richcompare */
    0,                                          /* tp_weaklistoffset */
    0,                                          /* tp_iter */
    0,                                          /* tp_iternext */
    spamlist_methods,                           /* tp_methods */
    0,                                          /* tp_members */
    spamlist_getsets,                           /* tp_getset */
    &PyTuple_Type,             /* tp_base */
    0,                                          /* tp_dict */
    0,                                          /* tp_descr_get */
    0,                                          /* tp_descr_set */
    0,                                          /* tp_dictoffset */
    (initproc)spamlist_init,                    /* tp_init */
    0,                                          /* tp_alloc */
    0,                                          /* tp_new */
};


/*
static PyObject *
_sockaddr_tostr(const Py_sockaddr_t *addrbuf, Py_socklen_t addrlen)
{
	switch (addrbuf->sa_family)
	{
	case AF_INET:
		res = getnameinfo(addrbuf, addrlen, host, sizeof(host), 
						  serv, sizeof(serv), flags);
		if (res)
			return _socketio_gaierr(res);
		return PyString_FromFormat(":inet:%s:%s", host, serv);
	case AF_INET6:
}
*/
static const char *
_socketio_getmode(socketio *self)
{
	static char buf[8];
	char type;

	switch (self->socktype)
	{
	case SOCK_STREAM:
		type = 's'; break;
	case SOCK_SEQPACKET:
		type = 'p'; break;
	case SOCK_RDM:
		type = 'm'; break;
	case SOCK_DGRAM:
		type = 'd'; break;
	case SOCK_RAW:
		type = 'a'; break;
	default:
		type = '?';
	}
	sprintf(buf, "%s%s%c%c", self->readable ? "r" : "",
							 self->writable ? "w" : "",
							 'b', type);
	return buf;
}

static PyObject *
socket_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
{
    socketio *self;

    assert(type != NULL && type->tp_alloc != NULL);

    self = (socketio *) type->tp_alloc(type, 0);
    if (self)
	{
        self->fd = INVALID_SOCKFD;
        self->readable = 0;
        self->writable = 0;
        self->closefd = 1;
//		self->listening = 0;
		self->connecting = 0;
		self->urgentmode = 0;
        self->weakref = NULL;
    }

    return (PyObject *) self;
}
/*
static int
socket_init(socketio *self, PyObject *args, PyObject *kwds)
{
//	PySocketObject *self = (PySocketObject*) oself;
	static char *kwlist[] = {"socket", "mode", "closefd", "timeout", 
							 "backlog", "openargs", NULL};
	PyObject *nameobj;

	assert(PySocketIO_Check(self));
	if (!_socketio_isclosed(self))
		pass;

	if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|si:socketio",
									 kwlist, &nameobj, &mode, &closefd))
		return -1;

    if (PyFloat_Check(nameobj))
	{
        PyErr_SetString(PyExc_TypeError, "integer file descriptor expected");
        return -1;
    }

    fd = PyLong_AsLong(nameobj);
    if (fd < 0)
	{
        if (!PyErr_Occurred())
		{
            PyErr_SetString(PyExc_ValueError, "negative file descriptor");
            return -1;
        }
        PyErr_Clear();
    }
	Py_BEGIN_ALLOW_THREADS
	fd = socket(family, socktype, proto);
	Py_END_ALLOW_THREADS

	if (fd == INVALID_SOCKFD)
		pass;
	self->fd = fd;
	if (timeout >= 0.0)
		sock_settimeout(timeout);
	if (passive)
	{
		bind(fd, &sockaddr, sizeof(sockaddr));
		listen(fd, backlog);
	}
	else
	{
		if (connect(fd, &sockaddr, sizeof(sockaddr)))
		{
			err = sockerr;
#ifdef MS_WINDOWS
			if (err == WSAEWOULDBLOCK)
#else
			if (err == EINPROGRESS || err == EINTR)
#endif
			
	}


}
*/
static int
socketio_traverse(socketio *self, visitproc visit, void *arg)
{
	assert(PySocketIO_Check(self));
    Py_VISIT(self->dict);
    return 0;
}

static int
socketio_clear(socketio *self)
{
	assert(PySocketIO_Check(self));
    Py_CLEAR(self->dict);
    return 0;
}

static void
socketio_dealloc(socketio *self)
{
	assert(PySocketIO_Check(self));
    if (_PyIOBase_finalize((PyObject *) self))
        return;
    _PyObject_GC_UNTRACK(self);
    if (self->weakref)
        PyObject_ClearWeakRefs((PyObject *) self);
    Py_CLEAR(self->dict);
    Py_TYPE(self)->tp_free((PyObject *) self);
}

static PyObject *
socketio_repr(socketio *self)
{
    PyObject *nameobj, *res;

	assert(PySocketIO_Check(self));
    if (_socketio_isclosed(self))
        return PyString_FromFormat("<%s [closed]>", 
									Py_TYPE(self)->tp_name);

    nameobj = PyObject_GetAttrString((PyObject *) self, "name");
    if (!nameobj)
	{
        if (PyErr_ExceptionMatches(PyExc_AttributeError))
            PyErr_Clear();
        else
            return NULL;
        res = PyString_FromFormat("<%s fd=%d mode='%s'>",
								   Py_TYPE(self)->tp_name,
                                   self->fd, _socketio_getmode(self));
    }
    else
	{
        PyObject *repr = PyObject_Repr(nameobj);
        Py_DECREF(nameobj);
        if (!repr)
            return NULL;
        res = PyString_FromFormat("<%s name=%s mode='%s'>",
								   Py_TYPE(self)->tp_name,
                                   PyString_AS_STRING(repr),
                                   _socketio_getmode(self));
        Py_DECREF(repr);
    }
    return res;
}

static PyObject *
passivesocket_iter(socketio *self)
{
	assert(PySocketIO_Check(self));
    if (_socketio_isclosed(self))
        return _socketio_closerr();

	Py_INCREF(self);
	return self;
}

static PyObject *
passivesocket_iternext(socketio *self)
{
	assert(PySocketIO_Check(self));
    if (_socketio_isclosed(self))
		return NULL;

	return PyObject_CallMethodObjArgs(self, "accept", NULL);
}

/* _socketio_atmark should return 1 if the next byte to be read is at the 
   urgent mark, 0 if it is not at the urgent mark, and -1 on error. It only
   makes sense to call this method when urgentmode has been set by one of
   the IOPoller methods. */

static int
_socketio_atmark(socketio *self)
{
	int res, value = 0;

#if defined(PLAT_WINDOWS)
	res = ioctlsocket(self->fd, SIOCATMARK, &value);
	if (res == 0)
		res = value ? 1 : 0;
#elif defined(PLAT_POSIX)
	/* Apparently sockatmark is broken in glibc before version 2.4,
	   so we should probably check for that somehow. */
	res = sockatmark(self->fd);
#else
	res = ioctl(self->fd, SIOCATMARK, &value);
	if (res == 0)
		res = value ? 1 : 0;
#endif

	return res;
}

static PyObject *
_socketio_recv(socketio *self, Py_buffer *buffer, Py_ssize_t size, int flags)
{
    PyObject *object = NULL;
    Py_ssize_t buflen, count;
	void *bufptr;

	buflen = buffer ? buffer->len : size;
	/* It seems that (at least on Linux) the call to recv when sockatmark
	   is true will return the last urgent mode byte along with any non-urgent
	   bytes following it. So, when sockatmark is true we will limit recv to
	   return at most 1 byte, then the next time through we will be out of
	   urgent mode. */
	if (self->urgentmode && _socketio_atmark(self) == 1 && buflen > 1)
		buflen = 1;
	if (buffer)
		bufptr = buffer->buf;
	else
	{
		object = PyBytes_FromStringAndSize(NULL, buflen);
		if (!object)
			return NULL;
		bufptr = PyBytes_AS_STRING(object);
	}

    Py_BEGIN_ALLOW_THREADS
    count = recv(self->fd, bufptr, buflen, flags);
    Py_END_ALLOW_THREADS

	if (buffer)
		PyBuffer_Release(buffer);
	if (count < 0)
	{
        Py_XDECREF(object);
        if (BLOCKING_ERROR)
            Py_RETURN_NONE;
		else
			return _socketio_error();
    }

	if (buffer)
	    object = PyInt_FromSsize_t(count);
	else if (count < buflen)
		_PyBytes_Resize(&object, count)
	return object;
}

static PyObject *
socketio_recv(socketio *self, PyObject *args)
{
    Py_ssize_t size = -1;
	int flags = 0;

	assert(PySocketIO_Check(self));
    if (_socketio_isclosed(self))
        return _socketio_closerror();
	if (!_socketio_isreadable(self))
		return _socketio_moderror("reading");

    if (!PyArg_ParseTuple(args, "n|i:recv", &size, &flags))
        return NULL;
	if (size < 0)
        return PyErr_Format(PyExc_ValueError, "negative buffersize in recv");

	return _socketio_recv(self, NULL, size, flags);
}

static PyObject *
socketio_read(socketio *self, PyObject *args)
{
    Py_ssize_t size = -1;

	assert(PySocketIO_Check(self));
    if (_socketio_isclosed(self))
        return _socketio_closerror();
	if (!_socketio_isreadable(self))
		return _socketio_moderror("reading");

    if (!PyArg_ParseTuple(args, "|O&:read", &_PyIO_ConvertSsize_t, &size))
        return NULL;
    if (size < 0)
        return socketio_readall(self);

	return _socketio_recv(self, NULL, size, 0);
}
/*
static PyObject *
_socketio_recv_buffer(Py_sockfd_t sockfd, Py_buffer *buffer, int flags)
{
    Py_ssize_t count, size = buffer->len;

	if (self->urgentmode && _socketio_atmark(self) == 1)
		size = 1;

	Py_BEGIN_ALLOW_THREADS
    count = recv(sockfd, buffer->buf, size, flags);
    Py_END_ALLOW_THREADS

	PyBuffer_Release(buffer);
    if (count < 0)
	{
        if (BLOCKING_ERROR)
            Py_RETURN_NONE;
		else
			return _socketio_error();
    }

    return PyInt_FromSsize_t(count);
}
*/
static PyObject *
socketio_recvinto(socketio *self, PyObject *args)
{
    Py_buffer buffer;
	int flags = 0;

	assert(PySocketIO_Check(self));
    if (_socketio_isclosed(self))
        return _socketio_closerror();
	if (!_socketio_isreadable(self))
		return _socketio_moderror("reading");

    if (!PyArg_ParseTuple(args, "w*|i:recvinto", &buffer, &flags))
        return NULL;

	return _socketio_recv(self, &buffer, 0, flags);
}

static PyObject *
socketio_readinto(socketio *self, PyObject *args)
{
    Py_buffer buffer;

	assert(PySocketIO_Check(self));
    if (_socketio_isclosed(self))
        return _socketio_closerror();
	if (!_socketio_isreadable(self))
		return _socketio_moderror("reading");

    if (!PyArg_ParseTuple(args, "w*:readinto", &buffer))
        return NULL;

	return _socketio_recv(self, &buffer, 0, 0);
}

static PyObject *
_socketio_send(socketio *self, Py_buffer *buffer, int flags)
{
    Py_ssize_t count;

    Py_BEGIN_ALLOW_THREADS
    count = send(self->fd, buffer->buf, buffer->len, flags);
    Py_END_ALLOW_THREADS

    PyBuffer_Release(buffer);
    if (count < 0)
	{
        if (BLOCKING_ERROR)
            Py_RETURN_NONE;
		else
			return _socketio_ioerror();
    }

    return PyInt_FromSsize_t(count);
}

static PyObject *
socket_send(socketio *self, PyObject *args)
{
    Py_buffer buffer;
	int flags = 0;

	assert(PySocketIO_Check(self));
    if (_socketio_isclosed(self))
        return _socketio_closerror();
	if (!_socketio_iswritable(self))
		return _socketio_moderror("writing");

    if (!PyArg_ParseTuple(args, "s*|i:send", &buffer, &flags))
        return NULL;

	return _socketio_send(self, &buffer, flags);
}

static PyObject *
socket_write(socketio *self, PyObject *args)
{
    Py_buffer buffer;

	assert(PySocketIO_Check(self));
    if (_socketio_isclosed(self))
        return _socketio_closerror();
	if (!_socketio_iswritable(self))
		return _socketio_moderror("writing");

    if (!PyArg_ParseTuple(args, "s*:write", &buffer))
        return NULL;

	return _socketio_send(self, &buffer, 0);
}

static PyObject *
socketio_getsockopt(socketio *self, PyObject *args)
{
	int res, level, option, intval;
	PyObject *objval = NULL;
	Py_ssize_t size = 0;
	char *bufptr;
	int buflen;

	assert(PySocketIO_Check(self));
    if (_socketio_isclosed(self))
        return _socketio_closerr();

    if (!PyArg_ParseTuple(args, "i|in:getsockopt", &level, &option, &size))
        return NULL;

	if (PyTuple_Size(args) == 1)
	{
		option = level;
		level = SOL_SOCKET;
	}
	if (size == 0)
	{
		intval = 0;
		bufptr = &intval;
		buflen = sizeof(intval);
	}
	else
	{
		objval = PyBytes_FromStringAndSize(NULL, size);
		if (!objval)
			return NULL;
		bufptr = PyBytes_AS_STRING(objval);
		buflen = size;
	}

	Py_BEGIN_ALLOW_THREADS
	res = getsockopt(self->fd, level, option, bufptr, &buflen)
	Py_END_ALLOW_THREADS

	if (res)
	{
		Py_XDECREF(objval);
		return _socketio_ioerr();
	}

	if (size == 0)
		objval = PyInt_FromLong(intval);
	else
		_PyBytes_Resize(&objval, buflen);
	return objval;
}

PyDoc_STRVAR(getsockopt_doc,
"getsockopt([level=SOL_SOCKET,] option[, buffersize]) -> value\n\
\n\
Get a socket option.  See the OS manual for level and option.\n\
If a nonzero buffersize argument is given, the return value is a\n\
byte string of that length; otherwise it is an integer.");

static PyObject *
socketio_setsockopt(socketio *self, PyObject *args)
{
	int res, level, option, intval;
	PyObject *objval;
	char *bufptr;
	int buflen;

	assert(PySocketIO_Check(self));
    if (_socketio_isclosed(self))
        return _socketio_closerr();

    if (!PyArg_ParseTuple(args, "iiO:setsockopt", &level, &option, &objval))
        return NULL;

	if (PyInt_Check(objval))
	{
		intval = PyInt_AsLong(objval);
		bufptr = &intval;
		buflen = sizeof(intval);
	}
	else if (PyBytes_Check(objval))
	{
		bufptr = PyBytes_AsBytes(objval);
		buflen = PyBytes_Size(objval);
	}
	else
		return PyErr_Format(PyExc_TypeError, "option value must be integer or bytes");

	Py_BEGIN_ALLOW_THREADS
	res = setsockopt(self->fd, level, option, bufptr, buflen)
	Py_END_ALLOW_THREADS

	if (res)
		return _socketio_ioerr();
	Py_RETURN_NONE;
}

PyDoc_STRVAR(setsockopt_doc,
"setsockopt(level, option, value)\n\
\n\
Set a socket option.  See the OS manual for level and option.\n\
The value argument can either be an integer or byte string.");

static PyObject *
socketio_getsockname(socketio *self, PyObject *args)
{
	Py_sockaddr_t addrbuf = {0};
	Py_socklen_t addrlen = sizeof(addrbuf);
	int res;

    Py_BEGIN_ALLOW_THREADS
	res = getsockname(self->fd, (struct sockaddr *) &addrbuf, &addrlen);
    Py_END_ALLOW_THREADS

	if (res)
		return _socketio_ioerr();
	return _sockaddr_tostr(&addrbuf, addrlen);
}

static PyObject *
socketio_shutdown(socketio *self, PyObject *args)
{
	int res, how;

	assert(PySocketIO_Check(self));
    if (_socketio_isclosed(self))
        return _socketio_closerr();

    if (!PyArg_ParseTuple(args, "i:shutdown", &how))
        return NULL;

	Py_BEGIN_ALLOW_THREADS
	res = shutdown(self->fd, how);
	Py_END_ALLOW_THREADS

	if (res)
		return _socketio_ioerr();

	if (how == SHUT_RD || how == SHUT_RDWR)
		self->readable = 0;
	if (how == SHUT_WR || how == SHUT_RDWR)
		self->writable = 0;
	Py_RETURN_NONE;
}

PyDoc_STRVAR(shutdown_doc,
"shutdown(how)\n\
\n\
Shut down the reading side of the socket (how == SHUT_RD), the writing side\n\
of the socket (how == SHUT_WR), or both ends (how == SHUT_RDWR).");

static PyObject *
socketio_gettimeout(socketio *self, void *closure)
{
	assert(PySocket_Check(self));
    if (_socketio_isclosed(self))
        return _socketio_closerror();
    return PyFloat_FromDouble(self->timeout / 1000.0);
}

static int
_socketio_settimeout(socketio *self, int timeout)
{
	int res;
	struct timeval tval;
	int nonblock = (timeout == 0);
	if (timeout < 0)
		timeout = 0;
	tval.tv_sec = timeout / 1000;
	tval.tv_usec = (timeout - tval.tv_sec * 1000) * 1000;

    Py_BEGIN_ALLOW_THREADS
#if defined(MS_WINDOWS)
	res = ioctlsocket(self->fd, FIONBIO, &nonblock);
	if (!res)
		res = setsockopt(self->fd, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout));
	if (!res)
		res = setsockopt(self->fd, SOL_SOCKET, SO_SNDTIMEO, &timeout, sizeof(timeout));
#elif defined(_POSIX_VERSION) && (_POSIX_VERSION >= 200112L)
	res = fcntl(self->sock, F_GETFL);
	if (res != -1)
		res = fcntl(self->sock, F_SETFL, nonblock ? res | O_NONBLOCK : res & ~O_NONBLOCK);
	if (res != -1)
		res = setsockopt(self->fd, SOL_SOCKET, SO_RCVTIMEO, &tval, sizeof(tval));
	if (res != -1)
		res = setsockopt(self->fd, SOL_SOCKET, SO_SNDTIMEO, &tval, sizeof(tval));
#endif
    Py_END_ALLOW_THREADS
}

static int
socketio_settimeout(socketio *self, PyObject *value, void *closure)
{
	int timeout = -1;

	assert(PySocket_Check(self));
    if (_socketio_isclosed(self))
        return _socketio_closerror(), -1;

	if (!value)
		return PyErr_SetString(PyExc_AttributeError, "can't delete frobnoz"), -1;
	else if (value != Py_None)
	{
		if (PyInt_Check(value))
			timeout = PyInt_AS_LONG(value) * 1000;
		else if (PyFloat_Check(value))
			timeout = (int) (PyFloat_AS_DOUBLE(value) * 1000.0);
		else
			return PyErr_SetString(PyExc_TypeError, "timeout must be a number or None"), -1;
		if (timeout < 0)
			return PyErr_SetString(PyExc_ValueError, "timeout can't be negative"), -1;
	}

	return _socketio_settimeout(self, timeout);
}

static PyObject *
socketio_readable(socketio *self)
{
	assert(PySocketIO_Check(self));
    if (_socketio_isclosed(self))
        return _socketio_closerror();
    return PyBool_FromLong(_socketio_isreadable(self));
}

static PyObject *
socketio_writable(socketio *self)
{
	assert(PySocketIO_Check(self));
    if (_socketio_isclosed(self))
        return _socketio_closerror();
    return PyBool_FromLong(_socketio_iswritable(self));
}

static PyObject *
socketio_fileno(socketio *self)
{
	assert(PySocketIO_Check(self));
    if (_socketio_isclosed(self))
		return _socketio_closerror();
    return PyInt_FromLong(self->fd);
}

PyDoc_STRVAR(fileno_doc,
"fileno() -> int. \"file descriptor\".\n"
"\n"
"This is needed for lower-level file interfaces, such the fcntl module.");

static PyObject *
socketio_getclosed(socketio *self, void *closure)
{
	assert(PySocketIO_Check(self));
    return PyBool_FromLong(_socketio_isclosed(self));
}

PyDoc_STRVAR(closed_doc, "True if the socket is closed");

static PyObject *
socketio_getclosefd(socketio *self, void *closure)
{
	assert(PySocketIO_Check(self));
    if (_socketio_isclosed(self))
		return _socketio_closerror();
    return PyBool_FromLong(self->closefd);
}

PyDoc_STRVAR(closefd_doc, "True if the socket descriptor will be closed");

static PyObject *
socketio_getmode(socketio *self, void *closure)
{
	assert(PySocketIO_Check(self));
    if (_socketio_isclosed(self))
		return _socketio_closerror();
    return PyString_FromString(_socketio_getmode(self));
}

PyDoc_STRVAR(mode_doc, "String giving the socket mode");

static PyObject *
socketio_geturgent(socketio *self, void *closure)
{
	assert(PySocketIO_Check(self));
    if (_socketio_isclosed(self))
		return _socketio_closerror();
    return PyBool_FromLong(self->urgentmode);
}

PyDoc_STRVAR(urgent_doc, "String giving the socket mode");

static PyMethodDef socketio_methods[] =
{
	/* SocketIO specific methods */
    {"getsockopt",  (PyCFunction) socketio_getsockopt,  METH_VARARGS, getsockopt_doc},
    {"setsockopt",  (PyCFunction) socketio_setsockopt,  METH_VARARGS, setsockopt_doc},
    {"getsockname", (PyCFunction) socketio_getsockname, METH_NOARGS,  getsockname_doc},
    {"getpeername", (PyCFunction) socketio_getpeername, METH_NOARGS,  getpeername_doc},
    {"shutdown", (PyCFunction) socketio_shutdown, METH_VARARGS, shutdown_doc},
    {"recv",     (PyCFunction) socketio_recv,     METH_VARARGS, recv_doc},
    {"recvinto", (PyCFunction) socketio_recvinto, METH_VARARGS, recv_doc},
    {"send",     (PyCFunction) socketio_send,     METH_VARARGS, send_doc},
	/* RawIO generic methods */
    {"read",     (PyCFunction) socketio_read,     METH_VARARGS, read_doc},
    {"readinto", (PyCFunction) socketio_readinto, METH_VARARGS, readinto_doc},
    {"readall",  (PyCFunction) socketio_readall,  METH_NOARGS,  readall_doc},
    {"write",    (PyCFunction) socketio_write,    METH_VARARGS, write_doc},
    {"close",    (PyCFunction) socketio_close,    METH_NOARGS,  close_doc},
    {"readable", (PyCFunction) socketio_readable, METH_NOARGS,  readable_doc},
    {"writable", (PyCFunction) socketio_writable, METH_NOARGS,  writable_doc},
    {"fileno",   (PyCFunction) socketio_fileno,   METH_NOARGS,  fileno_doc},
    {NULL}
};

static PyGetSetDef socketio_getsets[] =
{
    {"closed",  (getter) socketio_getclosed,  NULL, closed_doc},
    {"closefd", (getter) socketio_getclosefd, NULL, closefd_doc},
    {"mode",    (getter) socketio_getmode,    NULL, mode_doc},
    {"urgent",  (getter) socketio_geturgent,  NULL, urgent_doc},
    {"timeout", (getter) socketio_gettimeout, (setter) socketio_settimeout, timeout_doc},
    {NULL}
};

static PyMethodDef passivesocket_methods[] =
{
    {"getsockopt",  (PyCFunction) socketio_getsockopt,  METH_VARARGS, getsockopt_doc},
    {"setsockopt",  (PyCFunction) socketio_setsockopt,  METH_VARARGS, setsockopt_doc},
    {"getsockname", (PyCFunction) socketio_getsockname, METH_NOARGS,  getsockname_doc},
    {"getpeername", (PyCFunction) socketio_getpeername, METH_NOARGS,  getpeername_doc},
    {"accept",   (PyCFunction) socketio_accept,   METH_NOARGS,  accept_doc},
    {"close",    (PyCFunction) socketio_close,    METH_NOARGS,  close_doc},
    {"readable", (PyCFunction) socketio_readable, METH_NOARGS,  readable_doc},
    {"writable", (PyCFunction) socketio_writable, METH_NOARGS,  writable_doc},
    {"fileno",   (PyCFunction) socketio_fileno,   METH_NOARGS,  fileno_doc},
    {NULL}
};

static PyGetSetDef passivesocket_getsets[] =
{
    {"closed",  (getter) socketio_getclosed,  NULL, closed_doc},
    {"closefd", (getter) socketio_getclosefd, NULL, closefd_doc},
    {"mode",    (getter) socketio_getmode,    NULL, mode_doc},
    {"timeout", (getter) socketio_gettimeout, (setter) socketio_settimeout, timeout_doc},
    {NULL}
};

PyTypeObject PySocketIO_Type =
{
    PyVarObject_HEAD_INIT(NULL, 0)
    "_ipcio.SocketIO",                          /* tp_name */
    sizeof(socketio),                           /* tp_basicsize */
    0,                                          /* tp_itemsize */
    (destructor) socketio_dealloc,              /* tp_dealloc */
    0,                                          /* tp_print */
    0,                                          /* tp_getattr */
    0,                                          /* tp_setattr */
    0,                                          /* tp_reserved */
    (reprfunc) socketio_repr,                   /* tp_repr */
    0,                                          /* tp_as_number */
    0,                                          /* tp_as_sequence */
    0,                                          /* tp_as_mapping */
    0,                                          /* tp_hash */
    0,                                          /* tp_call */
    0,                                          /* tp_str */
    PyObject_GenericGetAttr,                    /* tp_getattro */
    0,                                          /* tp_setattro */
    0,                                          /* tp_as_buffer */
    Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE
                       | Py_TPFLAGS_HAVE_GC,    /* tp_flags */
    socketio_doc,                               /* tp_doc */
    (traverseproc) socketio_traverse,           /* tp_traverse */
    (inquiry) socketio_clear,                   /* tp_clear */
    0,                                          /* tp_richcompare */
    offsetof(socketio, weakref),                /* tp_weaklistoffset */
    0,                                          /* tp_iter */
    0,                                          /* tp_iternext */
    socketio_methods,                           /* tp_methods */
    0,                                          /* tp_members */
    socketio_getsets,                           /* tp_getset */
    0,                                          /* tp_base */
    0,                                          /* tp_dict */
    0,                                          /* tp_descr_get */
    0,                                          /* tp_descr_set */
    offsetof(socketio, dict),                   /* tp_dictoffset */
    socketio_init,                              /* tp_init */
    PyType_GenericAlloc,                        /* tp_alloc */
    socketio_new,                               /* tp_new */
    PyObject_GC_Del,                            /* tp_free */
};

PyTypeObject PyPassiveSocket_Type =
{
    PyVarObject_HEAD_INIT(NULL, 0)
    "_ipcio.PassiveSocket",                     /* tp_name */
    sizeof(socketio),                           /* tp_basicsize */
    0,                                          /* tp_itemsize */
    (destructor) socketio_dealloc,              /* tp_dealloc */
    0,                                          /* tp_print */
    0,                                          /* tp_getattr */
    0,                                          /* tp_setattr */
    0,                                          /* tp_reserved */
    (reprfunc) socketio_repr,                   /* tp_repr */
    0,                                          /* tp_as_number */
    0,                                          /* tp_as_sequence */
    0,                                          /* tp_as_mapping */
    0,                                          /* tp_hash */
    0,                                          /* tp_call */
    0,                                          /* tp_str */
    PyObject_GenericGetAttr,                    /* tp_getattro */
    0,                                          /* tp_setattro */
    0,                                          /* tp_as_buffer */
    Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE
                       | Py_TPFLAGS_HAVE_GC,    /* tp_flags */
    passivesocket_doc,                          /* tp_doc */
    (traverseproc) socketio_traverse,           /* tp_traverse */
    (inquiry) socketio_clear,                   /* tp_clear */
    0,                                          /* tp_richcompare */
    offsetof(socketio, weakref),                /* tp_weaklistoffset */
    passivesocket_iter,                         /* tp_iter */
    passivesocket_iternext,                     /* tp_iternext */
    passivesocket_methods,                      /* tp_methods */
    0,                                          /* tp_members */
    passivesocket_getsets,                      /* tp_getset */
    0,                                          /* tp_base */
    0,                                          /* tp_dict */
    0,                                          /* tp_descr_get */
    0,                                          /* tp_descr_set */
    offsetof(socketio, dict),                   /* tp_dictoffset */
    socketio_init,                              /* tp_init */
    PyType_GenericAlloc,                        /* tp_alloc */
    socketio_new,                               /* tp_new */
    PyObject_GC_Del,                            /* tp_free */
};

static PyObject *
ex_foo(PyObject *self, PyObject *args)
{
	printf("Hello, world\n");
	Py_INCREF(Py_None);
	return Py_None;
}

static PyMethodDef netio_methods[] =
{
	{"foo", ex_foo, METH_VARARGS, "foo() doc string"},
	{NULL}
};


#ifdef MS_WINDOWS

static void
os_cleanup(void)
{
    WSACleanup();
}

static int
os_init(PyObject *mod)
{
	WSADATA wsaData = { 0 };
	int ret;

	ret = WSAStartup(WINSOCK_VERSION, &wsaData);
	if (ret)
	{
		PyErr_SetFromWindowsErr(ret);
		return -1;
	}

	PyModule_AddIntConstant(mod, "_WINSOCK_VERSION", wsaData.wVersion);
	PyModule_AddStringConstant(mod, "_WINSOCK_DESCRIPTION", wsaData.szDescription);
	Py_AtExit(os_cleanup);
	return 0;
}

#else

static int
os_init(PyObject *)
{
}

#endif /* MS_WINDOWS */

PyMODINIT_FUNC
init_netio()
{
	PyObject *mod;

	mod = Py_InitModule3("_netio", netio_methods, netio_doc);
	if (!mod)
		goto fail;

	if (os_init(mod))
		goto fail;

	PySocketIO_Type.tp_base = &PyRawIOBase_Type;
	if (PyType_Ready(&PySocketIO_Type))
        goto fail;
	Py_INCREF(&PySocketIO_Type);
    if (PyModule_AddObject(mod, "SocketIO", (PyObject *) &PySocketIO_Type))
		goto fail;

	PyBufferedSequential_Type.tp_base = &PyBufferedIOBase_Type;
	if (PyType_Ready(&PyBufferedSequential_Type))
        goto fail;
	Py_INCREF(&PyBufferedSequential_Type);
    if (PyModule_AddObject(mod, "BufferedSequential", (PyObject *) &PyBufferedSequential_Type))
		goto fail;

	if (PyType_Ready(&PyIOPoller_Type))
        goto fail;
	Py_INCREF(&PyIOPoller_Type);
    if (PyModule_AddObject(mod, "IOPoller", (PyObject *) &PyIOPoller_Type))
		goto fail;

	if (PyType_Ready(&PyAsyncIOPoller_Type))
        goto fail;
	Py_INCREF(&PyAsyncIOPoller_Type);
    if (PyModule_AddObject(mod, "AsyncIOPoller", (PyObject *) &PyAsyncIOPoller_Type))
		goto fail;

	return;

fail:
	Py_XDECREF(mod);
}
